package com.google.code.stalkernet.example.ui.action;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.AbstractAction;
import javax.swing.JOptionPane;

import com.google.code.stalkernet.async.Async.Callback;
import com.google.code.stalkernet.async.factory.AsyncFactory;
import com.google.code.stalkernet.example.ui.util.SocketAddressUtils;
import com.google.code.stalkernet.node.Node;
import com.google.code.stalkernet.peer.Peer;
import com.google.code.stalkernet.reactor.Reactor;

public class ConnectPeerAction extends AbstractAction
{
    private final static Logger logger = Logger.getLogger (
        ConnectPeerAction.class.getName ());

    private final AsyncFactory <? super SocketAddress, ? extends Peer>
        peerFactory;
    private final Node node;
    private final Reactor reactor;

    public ConnectPeerAction (
        AsyncFactory <? super SocketAddress, ? extends Peer> peerFactory,
        Node node, Reactor reactor)
    {
        super ("Connect Peer");

        if (peerFactory == null)
            throw new IllegalArgumentException ("Peer factory is null");

        if (node == null)
            throw new IllegalArgumentException ("Node is null");

        if (reactor == null)
            throw new IllegalArgumentException ("Reactor is null");

        this.peerFactory = peerFactory;
        this.node = node;
        this.reactor = reactor;
    }

    @Override
    public void actionPerformed (ActionEvent e)
    {
        String socketAddressString = JOptionPane.showInputDialog(
            (Component)e.getSource (), "Server socket address:",
            "Add Server Socket Address", JOptionPane.PLAIN_MESSAGE);

        if (socketAddressString != null)
        {
            try
            {
                final SocketAddress socketAddress =
                    SocketAddressUtils.parseSocketAddress (
                        socketAddressString);

                reactor.invoke (
                    new Runnable ()
                    {
                        @Override
                        public void run ()
                        {
                            peerFactory.produce (
                                socketAddress,
                                new MyConnectorCallback (socketAddress));
                        }
                    });
            }
            catch (UnknownHostException ex)
            {
                logger.log (
                    Level.SEVERE,
                    "Host not found: " + socketAddressString,
                    ex);
            }
            catch (IllegalArgumentException ex)
            {
                logger.log (
                    Level.SEVERE,
                    "Invalid socket address: " + socketAddressString,
                    ex);
            }
        }
    }

    private class MyConnectorCallback implements Callback <Peer>
    {
        private final SocketAddress socketAddress;

        public MyConnectorCallback (SocketAddress socketAddress)
        {
            if (socketAddress == null)
                throw new IllegalArgumentException ("Socket address is null");

            this.socketAddress = socketAddress;
        }

        @Override
        public void onSuccess (Peer peer)
        {
            node.addPeer (peer);
        }

        @Override
        public void onError (Throwable error)
        {
            logger.log (
                Level.WARNING,
                "Cannot connect to " + socketAddress,
                error);
        }
    }
}
